import logging
import re
import uuid
import sys
import os
import urllib2

class Error(Exception):
    """Base class for exceptions in this module."""
    pass
    
from google.appengine.api        import memcache
from google.appengine.ext        import webapp
from google.appengine.ext        import db
from google.appengine.ext.webapp import template
from model                       import Mech

from BeautifulSoup               import BeautifulSoup

class IndexHandler(webapp.RequestHandler):

    def get(self):
        dropid  = self.request.get('d')
        home    = False if self.request.get('d') else True
        player  = self.request.get('p')
        action  = self.request.get('a')

        if not dropid: dropid = uuid.uuid4().hex

        mechlist = self.load_mechlist(dropid)

        if not player: 
            self.response.out.write(template.render('templates/index.html', {
                'starter': self.find_starter(mechlist),
                'mechs':   mechlist,
                'method': 'get',
                'home': home,
                'mech':    {'dropid': dropid, 'player': ''}
            }))

            return

        if action == "x":
            for m in mechlist:
                if m.player == player and m.player != self.find_starter(mechlist):
                    m.delete()
                    mechlist.remove(m)
                    self.update_cache(dropid, mechlist)
                    self.redirect("/?d="+dropid)

                    return

        self.process_request()

    def post(self):
        self.process_request()

    def process_request(self):
        dropid         = self.request.get('d'); 
        player         = self.request.get('p'); 
        mechtype       = self.request.get('m')
        loadout        = self.request.get('l')
        action         = self.request.get('a')
        players        = self.request.POST.getall('player')
        embedded       = self.request.get('e')

        if not dropid: dropid = uuid.uuid4().hex
        mechlist       = self.load_mechlist(dropid)
        mech           = {'dropid': dropid, 'player': ''}

        if player:
            self.process_actions( mechlist, dropid, players, action )
            mech           = self.save_mech(mechlist, dropid, loadout, mechtype, player)

            if not mech:
                mech       = self.add_mech(mechlist, dropid, loadout, mechtype, player)
                mechlist.append(mech)
                self.update_cache(dropid, mechlist)

        drop_totals    = self.calculate_drop_totals(mechlist)

        t = "templates/index.html"
        if embedded:
            t = "templates/mechlist.html"
        
        self.response.out.write(template.render(t, {
            'starter': self.find_starter(mechlist),
            'd': dropid,
            'mech': mech,
            'method': 'post',
            'mechs': mechlist,
            'tonnages': self.fetch_mechs_from_smurfy(),
            'allmechs': self.sorted_list_of_all_mechs(),
            'drop_totals': drop_totals
        }))

    #
    # Find the starter
    #
    def find_starter(self, mechlist):
        for m in mechlist:
            if m.starter == 1:
                return m.player

        return ""

    #
    # Process actions like Lance Assignment, Assign lead and Player removal
    #
    def process_actions(self, mechlist, dropid, players, action):

        starter = self.find_starter(mechlist)
        list_changed = 0

        if action and len(players) > 0:
            lance   = ""

            if action == "a":
                lance = "Alpha"
            if action == "b":
                lance = "Bravo"
            if action == "c":
                lance ="Charlie"
            if action == "d":
                lance = "Delta"
            if action == "r":
                lance = "Reserve"
            if action == "l":
                pass
            if action == "x":
                pass

            deleted = []
            for m in mechlist:
                if action == "l":
                    if m.player == players[0]:
                        m.starter = 1
                        mech = m
                    else:
                        m.starter = 0

                    m.put()
                    list_changed = 1

                if action == "x":
                    for p in players:
                        if m.player == p and m.player != starter:
                            deleted.append(m.player)
                            m.delete()
                            list_changed = 1

                if lance != "":
                    for p in players:
                        if m.player == p:
                            m.lance = lance
                            m.put()
                            list_changed = 1

            if len(deleted) > 0:
                for d in deleted:
                    for m in mechlist:
                        if d == m.player:
                            mechlist.remove(m)

            if list_changed:
                self.update_cache(dropid, mechlist)

    #
    # Save changes to an existing mech 
    #
    def save_mech(self, mechlist, dropid, loadout, mechtype, player):
        mech  = None
        mechcount = 0
        list_changed = 0

        for m in mechlist:
            mechcount = mechcount +1
            mech_changed = 0

            if m.player == player:
                if m.loadout != loadout: 
                    mech_changed = 1
                    list_changed = 1

                m.loadout = loadout

                if m.mechtype != mechtype: 
                    mech_changed = 1
                    list_changed = 1

                m.mechtype = mechtype

                mech = m

            if mech_changed: 
                mech.put()

        # nasty side effect
        if list_changed: 
            self.update_cache(dropid, mechlist)

        return mech

    #
    # Add a new mech 
    #
    def add_mech(self, mechlist, dropid, loadout, mechtype, player):
        mechcount = len(mechlist)
        mech = Mech(dropid=dropid, loadout=loadout, mechtype=mechtype, player=player, position = mechcount + 1, lance = "Reserve")

        if mechcount == 0:
            starter = player
            mech.starter = 1

        mech.put()

        return mech

    # 
    # A list of all mechs for display in the html-form
    #
    def sorted_list_of_all_mechs(self):
        mechlist = self.fetch_mechs_from_smurfy()
        allmechs = []
        
        for key in sorted(mechlist.iterkeys()):
            allmechs.append(key)
        
        return allmechs
        
    #
    # Get a list of all mechs and tonnage from the smurfy website,
    # cache for a day to avoid spamming
    #
    def fetch_mechs_from_smurfy(self):
    
        mechlist = memcache.get("smurfy")

        if mechlist is not None:
            return mechlist
        
        mechlist = {}
        tonnage = 0
        
        try:
            html = urllib2.urlopen("http://mwo.smurfy-net.de/")
        except urllib2.URLError, e:
            pass
            
        if html:
            logging.debug(html)
            soup = BeautifulSoup(html)
            for row in soup.findAll('tr'):
                if row.th:
                    tclass = row.th.get("class")
                    if tclass:
                        if u'mechs_family' == tclass:
                            tonnage = row.th.small.string
                            tonnage = int( re.sub("[\\(\\)]", "", tonnage) )
                
                if row.td:
                    link = row.td.a
                    if link:
                        href = link.get('href')
                        text = str(link.string)
                        if href and re.match("/mechlab#i=", href):
                            mechlist[text] = tonnage
                            
            mechlist[""] = 0
            
            memcache.set("smurfy", mechlist, 24 * 60 * 60)
         
        return mechlist
        
    #
    # Calculate tonnage totals
    #
    def calculate_drop_totals(self, mechlist):
        active_players = 0
        active_tonnage = 0
        total_players  = 0
        total_tonnage  = 0
        
        tonnages = self.fetch_mechs_from_smurfy()

        for m in mechlist:
            if m.lance:
                total_players = total_players + 1
                if m.mechtype:
                    total_tonnage = total_tonnage + tonnages[m.mechtype]
                if m.lance != "Reserve":
                    active_players = active_players + 1
                    if m.mechtype:
                        active_tonnage = active_tonnage + tonnages[m.mechtype]

        return {
            'active_players': active_players,
            'active_tonnage': active_tonnage,
            'total_players':  total_players,
            'total_tonnage':  total_tonnage
        }

    def invalidate_cache(self, dropid):
        memcache.delete(dropid)

    def update_cache(self, dropid, list):
        memcache.set(dropid, list, 5 * 60)

    def load_mechlist(self, dropid):
        list = memcache.get(dropid)

        if list is not None:
            return list

        query = db.GqlQuery('SELECT * FROM Mech WHERE dropid = :1 ORDER BY lance', dropid)
        list = []

        for i in query:
            list.append(i)

        memcache.add(dropid, list, 5 * 60)

        return list

class UnknownHandler(webapp.RequestHandler):

    def get(self):
        self.response.out.write(404)
        self.error(404)

class ForbiddenHandler(webapp.RequestHandler):

    def get(self):
        self.response.out.write(400)
        self.error(400)